package com.xinzi.apartment.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinzi.apartment.common.exception.InvalidParamException;
import com.xinzi.apartment.dao.StuInfoDao;
import com.xinzi.apartment.entity.DormitoryInfoEntity;
import com.xinzi.apartment.entity.StuInfoEntity;
import com.xinzi.apartment.service.DormitoryInfoService;
import com.xinzi.apartment.service.StuInfoService;
import com.xinzi.apartment.vo.StuDistributionVo;
import com.xinzi.apartment.common.exception.BizCodeEnum;
import com.xinzi.apartment.common.exception.NoEnoughDormitoryException;
import com.xinzi.apartment.common.utils.PageUtils;
import com.xinzi.apartment.common.utils.Query;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;


@Service("stuInfoService")
public class StuInfoServiceImpl extends ServiceImpl<StuInfoDao, StuInfoEntity> implements StuInfoService {

    private static final String ORDER_FILED_STU_CLASS = "stu_class";

    @Resource
    private StuInfoDao stuInfoDao;
    @Autowired
    private DormitoryInfoService dormitoryInfoService;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<StuInfoEntity> queryWrapper = new QueryWrapper<>();
        String key = (String) params.get("key");
        if (StringUtils.isNotBlank(key)) {
            queryWrapper.and(wrapper -> buildQueryCondition(key, wrapper));
        }
        IPage<StuInfoEntity> page = this.page(
                new Query<StuInfoEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }


    /**
     * 查询还没有分配寝室的所有学生
     * @param params
     * @return
     */
    @Override
    public PageUtils queryNotDistribute(Map<String, Object> params) {
        QueryWrapper<StuInfoEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.isNull("dormitory_id");
        String key = (String) params.get("key");
        if (StringUtils.isNotBlank(key)) {
            queryWrapper.and(wrapper -> buildQueryCondition(key, wrapper));
        }

        IPage<StuInfoEntity> page = this.page(
                new Query<StuInfoEntity>().getPage(params, ORDER_FILED_STU_CLASS, true),
                queryWrapper
        );

        return new PageUtils(page);
    }


    private void buildQueryCondition(String key, QueryWrapper<StuInfoEntity> queryWrapper) {
        queryWrapper.eq("sno", key).or()
                .eq("name", key).or()
                .eq("gender", key).or()
                .eq("nation", key).or()
                .eq("major", key).or()
                .eq("stu_class", key).or()
                .eq("phone", key).or()
                .eq("apartment_id", key).or()
                .eq("dormitory_id", key);
    }


    /**
     * 自动给学生分配宿舍
     */
    @Override
    @Transactional
    public List<StuDistributionVo> autoDistribute(Integer[] ids) throws NoEnoughDormitoryException, InvalidParamException {
        List<StuInfoEntity> stuInfoEntities = null;
        if (ids.length == 0) {
            // 对所有还没有分配过寝室的学生分配
            stuInfoEntities = this.list(
                    new QueryWrapper<StuInfoEntity>().isNull("dormitory_id").isNull("apartment_id")
            );
        } else {
            List<Integer> idList = Arrays.asList(ids);
            stuInfoEntities = this.listByIds(idList);
            // 参数效验
            checkParam(stuInfoEntities);
        }

        // 开始分配宿舍，分别为男女生分配
        List<StuDistributionVo> femaleResult = doAutoDistribution(1, stuInfoEntities);
        List<StuDistributionVo> maleResult = doAutoDistribution(0, stuInfoEntities);
        maleResult.addAll(femaleResult);
        return maleResult;
    }


    private void checkParam(List<StuInfoEntity> stuInfoEntities) throws InvalidParamException {
        for (StuInfoEntity stuInfoEntity : stuInfoEntities) {
            Integer dormitoryId = stuInfoEntity.getDormitoryId();
            Integer apartmentId = stuInfoEntity.getApartmentId();
            if (dormitoryId != null || apartmentId != null) {
                throw new InvalidParamException(BizCodeEnum.INVALID_PARAM.getMessage());
            }
        }
    }


    private List<StuDistributionVo> doAutoDistribution(int flag, List<StuInfoEntity> stuInfoEntities)
            throws NoEnoughDormitoryException {
        List<StuDistributionVo> result = new ArrayList<>();
        // 分别查询男生、女生所有还有空位的寝室
        List<DormitoryInfoEntity> dormitoryInfoEntities = null;
        if (flag == 0) {
            // 过滤女生，并按专业、班级、名族排序
            stuInfoEntities = stuInfoEntities.stream().filter(stuInfoEntity -> "f".equalsIgnoreCase(stuInfoEntity.getGender()))
                    .sorted(Comparator.comparing(StuInfoEntity::getMajor).thenComparing(StuInfoEntity::getStuClass)
                            .thenComparing(StuInfoEntity::getNation))
                    .collect(Collectors.toList());
            dormitoryInfoEntities = dormitoryInfoService.queryHasRemainDormitoriesByGenderFlag(0);
        } else if (flag == 1) {
            // 过滤男生，并按专业、班级、名族排序
            stuInfoEntities = stuInfoEntities.stream().filter(stuInfoEntity -> "m".equalsIgnoreCase(stuInfoEntity.getGender()))
                    .sorted(Comparator.comparing(StuInfoEntity::getMajor).thenComparing(StuInfoEntity::getStuClass)
                            .thenComparing(StuInfoEntity::getNation))
                    .collect(Collectors.toList());
            dormitoryInfoEntities = dormitoryInfoService.queryHasRemainDormitoriesByGenderFlag(1);
        }

        if (CollectionUtils.isEmpty(stuInfoEntities)) {
            // 无需分配
            return Collections.emptyList();
        }
        if (CollectionUtils.isEmpty(dormitoryInfoEntities)) {
            // 没有可分配的宿舍
            throw new NoEnoughDormitoryException(BizCodeEnum.NO_ENOUGH_DORMITORY.getMessage());
        }

        AtomicInteger index = new AtomicInteger(0);
        boolean hasFinish = false;
        for (DormitoryInfoEntity dormitoryInfoEntity : dormitoryInfoEntities) {
            if (hasFinish) {
                break;
            }
            // 拿到寝室剩余的空位，即需要分配的数量
            Integer remainNum = dormitoryInfoEntity.getRemainNum();
            // 分配
            for (int i = 0; i < remainNum; i++) {
                int stuIndex = index.getAndIncrement();
                if (stuIndex >= stuInfoEntities.size()) {
                    // 已经分配完，直接返回
                    dormitoryInfoEntity.setRemainNum(remainNum - i);
                    hasFinish = true;
                    break;
                }
                StuInfoEntity stuInfoEntity = stuInfoEntities.get(stuIndex);
                stuInfoEntity.setDormitoryId(dormitoryInfoEntity.getId());
                stuInfoEntity.setApartmentId(dormitoryInfoEntity.getFlatNo());
                // 构造分配结果
                StuDistributionVo stuDistributionVo = StuDistributionVo.builder()
                        .sno(stuInfoEntity.getSno())
                        .name(stuInfoEntity.getName())
                        .apartment_id(stuInfoEntity.getApartmentId())
                        .dormitory_id(stuInfoEntity.getDormitoryId())
                        .build();
                result.add(stuDistributionVo);
                // 设置剩余空位
                dormitoryInfoEntity.setRemainNum(0);
            }
        }
        if (index.get() < stuInfoEntities.size()) {
            // 不够分配
            throw new NoEnoughDormitoryException(BizCodeEnum.NO_ENOUGH_DORMITORY.getMessage());
        }
        // 保存
        dormitoryInfoService.updateBatchById(dormitoryInfoEntities);
        this.updateBatchById(stuInfoEntities);

        return result;
    }

}